#include "ez_log.h"
#include "ez_malloc.h"
#include "ez_net.h"
#include <stdlib.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>

#define NOTIFY_IDENT 0

typedef struct ez_kq_api_state_s {
    int kqfd;
    int evid;
    struct kevent events[0];
} ez_kq_api_state;

static inline void do_event_cmd(ez_event_loop_t *eventLoop) {
    ez_kq_api_state *state = eventLoop->apidata;
    log_info("evid: %d stop eventLoop!", state->evid);
    eventLoop->stop = 1;
}

static int api_kq_create(ez_event_loop_t *eventLoop) {
    ez_kq_api_state *state = ez_malloc(sizeof(ez_kq_api_state) + sizeof(struct kevent) * eventLoop->size);

    if (!state)
        return -1;

    state->kqfd = kqueue();
    if (state->kqfd == -1) {
        ez_free(state);
        return -1;
    }
    ez_net_set_closexec(state->kqfd);
    state->evid = NOTIFY_IDENT;
    eventLoop->apidata = state;
    return 0;
}

static void api_kq_delete(ez_event_loop_t *eventLoop) {
    ez_kq_api_state *state = eventLoop->apidata;

    close(state->kqfd);
    ez_free(state);
}

static int api_kq_add_event(ez_event_loop_t *eventLoop, int fd, int mask, int oldmask) {
    ez_kq_api_state *state = eventLoop->apidata;
    struct kevent ke;
    memset(&ke, 0, sizeof(ke));

    if (mask & AE_READABLE) {
        EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, NULL);
        if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1)
            return -1;
    }
    if (mask & AE_WRITABLE) {
        EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, NULL);
        if (kevent(state->kqfd, &ke, 1, NULL, 0, NULL) == -1)
            return -1;
    }
    return 0;
}

static void api_kq_del_event(ez_event_loop_t *eventLoop, int fd, int mask, int oldmask) {
    ez_kq_api_state *state = eventLoop->apidata;
    struct kevent ke;
    memset(&ke, 0, sizeof(ke));

    if (mask & AE_READABLE) {
        EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL);
        kevent(state->kqfd, &ke, 1, NULL, 0, NULL);
    }
    if (mask & AE_WRITABLE) {
        EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL);
        kevent(state->kqfd, &ke, 1, NULL, 0, NULL);
    }
}

static void api_kq_before(ez_event_loop_t *eventLoop) {
    ez_kq_api_state *state = eventLoop->apidata;

    struct kevent kev;
    memset(&kev, 0, sizeof(kev));

    kev.ident = state->evid;
    kev.filter = EVFILT_USER;
    kev.flags = EV_ADD | EV_CLEAR;

    if (kevent(state->kqfd, &kev, 1, NULL, 0, NULL) == -1) {
        log_warn("evid: %d EVFILT_USER ADD failed!", state->evid);
    } else {
        log_info("evid: %d EVFILT_USER ADD success!", state->evid);
    }
}

static void api_kq_after(ez_event_loop_t *eventLoop) {
    ez_kq_api_state *state = eventLoop->apidata;

    struct kevent kev;
    memset(&kev, 0, sizeof(kev));

    kev.ident = state->evid;
    kev.filter = EVFILT_USER;
    kev.flags = EV_DELETE;

    if (kevent(state->kqfd, &kev, 1, NULL, 0, NULL) == -1) {
        log_warn("evid: %d EVFILT_USER DELETE failed!", state->evid);
    } else {
        log_info("evid: %d EVFILT_USER DELETE success!", state->evid);
    }
}

static int api_kq_poll(ez_event_loop_t *eventLoop, int mstime) {
    ez_kq_api_state *state = eventLoop->apidata;
    int retval, numevents = 0;

    // timeout: 超时时间（毫秒）：
    // -1：无限阻塞，直到事件发生
    //  0：立即返回，即使无事件
    // >0：等待指定时长后超时返回
    //
    if (mstime >= 0) {
        struct timespec timeout;
        timeout.tv_sec = mstime / 1000;
        timeout.tv_nsec = (mstime % 1000) * 1000000;
        retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->size, &timeout);
    } else {
        // kevent timeout 传递NULL时，无限等待
        retval = kevent(state->kqfd, NULL, 0, state->events, eventLoop->size, NULL);
    }

    if (retval > 0) {
        int j;

        numevents = 0;

        for (j = 0; j < retval; j++) {
            struct kevent *e = state->events + j;

            // 优先处理 evfd 的事件
            if (e->ident == state->evid) {
                do_event_cmd(eventLoop);
                continue;
            }

            int mask = 0;

            if (e->filter == EVFILT_READ)
                mask |= AE_READABLE;
            else if (e->filter == EVFILT_WRITE)
                mask |= AE_WRITABLE;

            if (mask) {
                eventLoop->fired[numevents].fd = e->ident;
                eventLoop->fired[numevents].mask = mask;
                numevents++;
            }
        }
    } else if (retval == -1 && errno != EINTR) {
        log_error("aeApiPoll: kevent, %s", strerror(errno));
        exit(errno);
    }

    return numevents;
}

static void api_kq_stop(ez_event_loop_t *eventLoop) {
    ez_kq_api_state *state = eventLoop->apidata;

    struct kevent kev;
    memset(&kev, 0, sizeof(kev));

    kev.ident = state->evid;
    kev.filter = EVFILT_USER;
    kev.fflags = NOTE_TRIGGER;

    if (kevent(state->kqfd, &kev, 1, NULL, 0, NULL) == -1) {
        log_warn("evid: %d NOTE_TRIGGER failed , force stop eventLoop !", state->evid);
        eventLoop->stop = 1;
    } else {
        log_info("evid: %d NOTE_TRIGGER success!", state->evid);
    }
}

static ez_event_api_t module = {
        .name = "kqueue",
        .create = api_kq_create,
        .delete = api_kq_delete,

        .before = api_kq_before,
        .do_poll = api_kq_poll,
        .after = api_kq_after,
        .stop_poll = api_kq_stop,

        .add_event = api_kq_add_event,
        .del_event = api_kq_del_event
};
